home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / infoserv / gopher / Unix / gopher-gateways / techinfo / techinpher / v1.0 / transact.c.Z / transact.c
Encoding:
C/C++ Source or Header  |  1993-02-23  |  31.3 KB  |  1,223 lines

  1. /*
  2.   This software is copyrighted by the University of Pennsylvania.
  3.   Read COPYRIGHT for details.
  4.  
  5.   */
  6.  
  7. /*
  8.     Assume mostly the same strategy as the TechInfo server
  9.     and put all data into a buffer for the connection.
  10.  
  11.  
  12.     However, if the transaction requires connecting to gopher, then
  13.     fork a child, which will exec a new program with the right
  14.     argv[], and parent will create c_helper for the connection.
  15.     When the server receives the signal that
  16.     the child is done, it will read the child's output file,
  17.     translate it and write a TechInfo formatted file and call
  18.     nio_send_file().  Then remove_helper for the connection.
  19.  
  20.     If a file containing the gopher information is already
  21.     cached and isn't too old, use it instead of connecting to gopher.
  22.  
  23.     This is basically a complete rewrite of MIT's transact.c code.
  24.     I *really* disliked the extensive use of variables whose scope
  25.     was the entire module.  I like to pass parameter lists, even if
  26.     they get cumbersome.  Less confusion in the long run about
  27.     what gets used, what gets changed.
  28.     
  29.     */
  30.  
  31. #include <stdio.h>
  32. #include <unistd.h>
  33. #include <sys/file.h>
  34. #include <sys/types.h>
  35. #include <errno.h>
  36. #include <sys/time.h>
  37. #include <sys/wait.h>
  38. #include <sys/stat.h>
  39. #include <sys/resource.h>
  40. #include <strings.h>
  41. #include <sys/ioctl.h> /**/
  42. #include <signal.h>
  43.  
  44. #include "gophernodes.h"  /* new for techinpher gateway */
  45. #include "network.h"
  46. #include "pdb.h"
  47. #include "messages.h"
  48. #include "node.h"
  49.  
  50.  
  51. void admin(int numf, char **fields, int sock);
  52. void change_format(int *fmt, int sock, int numf, char **fields);
  53. void find(CONN *conn, char ch, int numf, char **fields );
  54. char *find_abbrev(char ch);
  55. void find_ver(CONN *conn, int numf, char **fields);
  56. int hdl_transact(CONN *conn);
  57. void log_trans(char *line);
  58. char *nodeinfostr (long nodeid, struct s1 *nd, int flgsfmt);
  59. void proc_trans(int numf, char ch, char **fields, CONN *conn);
  60. void send_connections(int sock);
  61. void send_empty_document(int sock);
  62. void send_empty_menu(int sock);
  63. void send_file(CONN *conn, char ttype, int numf, char **fields);
  64. void send_gopher_info(struct s1 *nd, int numf, char **fields, CONN *currcon);
  65. void send_help(int sock);
  66. void send_node(int flgsfmt, int sock, int numf, char **transfields);
  67. int send_reserved_node (char trans_type, int numf, char **fields, long nodeid, int flgsfmt, int cur_sock);
  68. void send_src_info(int sock);
  69. void send_servers_list(int sock);
  70. int test_admin(char *passwd);
  71. void traverse(CONN *conn, char ch, int numf, char **fields);
  72.  
  73.  
  74. extern char     *msglist[]; /* the list of error messages */
  75. extern int      debug;
  76. extern short    todaysdate;
  77. extern int      tables_changed;
  78. extern int      lastused_changed;
  79. extern void     parsefields();
  80. extern struct s1 *getnode_bynodeid(long nid);
  81. extern void     catch_child();
  82. extern void     nio_send_file(int s, char *fn, long stpt, long req, int isnlist);
  83. extern void     send_msg (char *string, int sock);
  84. extern int      sendbuf  (char *cp, int sz, int sock);
  85.  
  86. int             max_stale_time;  /* not used, but it's in server.c */
  87. static char     *curr_uid;  /* should get rid of this one too */
  88. static char     in_buff[BUFSIZ];  /* holds transaction from client */
  89. static char     nodeinfobuf[BUFSIZ];
  90.  
  91.  
  92.  
  93. struct gopherabbrev gopher_abbrev[] =
  94. {                  /* leaving out ERROR and DUPSRV */
  95.   {GOPHTYP_TEXT,        "."},
  96.   {GOPHTYP_MENU,        "/"},
  97.   {GOPHTYP_CSO,         " <CSO>"},
  98.   {GOPHTYP_MACHQX,      " <HQX>"},
  99.   {GOPHTYP_DOSBIN,      " <PC Bin>"},
  100.   {GOPHTYP_UUENC,       " <UUENC>"},
  101.   {GOPHTYP_SEARCH,      " <?>"},
  102.   {GOPHTYP_TELNET,      " <TEL>"},
  103.   {GOPHTYP_BINARY,      " <Bin>"},
  104.   {GOPHTYP_GIF,         " <Picture>"},
  105.   {GOPHTYP_IMAGE,       " <Picture>"},
  106.   {GOPHTYP_TN3270,      " <3270>"},
  107.   {GOPHTYP_SOUND,       " <)"},
  108.   {GOPHTYP_EVENT,       " <Event>"},
  109.   {GOPHTYP_CALENDAR,    " <Cal>"},
  110.   {GOPHTYP_MIME,        " <MIME>"},
  111.   {GOPHTYP_HTML,        " <HTML>"}
  112. };
  113. #define NUM_GOPHER_ABBREV 17
  114. #define gopher_abbrev_unk  " <Unknown>"
  115.  
  116.  
  117. int hdl_transact(CONN *conn)
  118. {
  119.     char            log_line[BUFSIZ];
  120.     int             length;
  121.     int             f;
  122.     int             cur_sock;
  123.     char            *transfields[MAX_TRANS_FIELDS];
  124.     int             num_transfields;
  125.     char            trans_type;
  126.  
  127.     curr_uid = conn->c_uid;
  128.     conn->c_trans_cnt++;
  129.     cur_sock = conn->c_socket;
  130.  
  131.     bzero(in_buff, BUFSIZ);
  132.     length = read(cur_sock, in_buff, BUFSIZ);
  133.     if (length <= 0) {
  134.       if (length)
  135.         perror("socket read");
  136.       /*                if (errno == EWOULDBLOCK)
  137.                 return 0;
  138.                 else 
  139.        This caused looping error -ST */
  140.       return 1;
  141.     }
  142.     if (in_buff[length - 1] == LF)
  143.         length--;
  144.     if (in_buff[length - 1] == CR)
  145.         length--;
  146.     in_buff[length] = '\0';
  147.     
  148.     trans_type = *in_buff;
  149.     parsefields(in_buff, transfields, &num_transfields, DLM, MAX_TRANS_FIELDS-1);
  150.     
  151.     if (length == 0 || trans_type == T_QUIT)
  152.       return 1;    /* quit and close connection */
  153.     
  154.     if (trans_type == T_ADMIN)
  155.       sprintf(log_line, "%s:%d:%c", curr_uid, cur_sock, trans_type);
  156.     else if (trans_type == T_TRYPROVIDER)
  157.       sprintf(log_line, "%s:%d:%c:%s", curr_uid,cur_sock,trans_type,
  158.           num_transfields < 2 ? "" : transfields[1]);
  159.     else {
  160.       sprintf (log_line, "%s:%d", curr_uid, cur_sock);
  161.       for (f=0; f < num_transfields; f++) {
  162.         strcat (log_line, ":");
  163.         strcat (log_line, transfields[f]);
  164.       }
  165.     }
  166.     log_trans(log_line);    /* log the transaction */
  167.     (void) proc_trans (num_transfields, trans_type, transfields, conn);
  168.     return 0;        /* ok */
  169. }
  170.  
  171.  
  172.  
  173. /* Switch on transaction type, calling handler procedure */
  174. static void
  175.   proc_trans(int num_transfields, char trans_type, char **transfields,
  176.          CONN *conn)
  177. {
  178.   int flgsfmt;
  179.   int cur_sock;
  180.  
  181.   flgsfmt = conn->c_output_fmt;
  182.   cur_sock = conn->c_socket;
  183.  
  184.   switch (trans_type)
  185.     {
  186.       /* No provider functions */
  187.     case T_ADDLINK:
  188.     case T_ADDNODE:
  189.     case T_CHG_SRC_INFO:
  190.     case T_GETFILE:
  191.     case T_REORDER_AFTER:    
  192.     case T_REORDER_BEFORE:    
  193.     case T_REPLACENODE:
  194.     case T_RMLINK:
  195.     case T_RMNODE:
  196.     case T_SOURCE:
  197.       send_msg(msglist[ NOT_AUTH ], cur_sock);
  198.       break;
  199.  
  200.     case T_TRYPROVIDER:
  201.       send_msg(msglist[ BAD_PASSWD ], cur_sock);
  202.       break;
  203.       
  204.     case T_NODE_FORMAT:
  205.       change_format ( &(conn->c_output_fmt), cur_sock, num_transfields, transfields );
  206.       break;
  207.       
  208.     case T_VERSION:    /* find the current version number */
  209.       find_ver(conn, num_transfields, transfields);
  210.       break;
  211.       
  212.     case T_SRC_INFO:
  213.       send_src_info(cur_sock); 
  214.       break;
  215.  
  216.     case T_SENDFILE:
  217.       send_file(conn, trans_type, num_transfields, transfields);
  218.       break;
  219.  
  220.     case T_SENDNODE:
  221.       send_node(flgsfmt, cur_sock, num_transfields, transfields);
  222.       break;
  223.       
  224.     case T_TRAVERSE:    /* do a traverse + return list */
  225.       traverse(conn, trans_type, num_transfields, transfields);
  226.       break;
  227.       
  228.     case T_HELP:
  229.       send_reserved_node (trans_type, num_transfields, transfields,
  230.               HELP_MENU_NODE, flgsfmt, cur_sock);
  231.       break;
  232.       
  233.     case T_FULL_TXT_SEARCH:     /* no difference in Gopher between the two */
  234.     case T_FIND:        /* do a find + return list */
  235.     case T_TITLE_SRCH:
  236.       find(conn, trans_type, num_transfields, transfields);
  237.       break;
  238.       
  239.  
  240.       /* ADMIN functions */
  241.     case T_ADMIN:
  242.       admin(num_transfields, transfields, cur_sock);
  243.       break;
  244.  
  245.     case T_ENDPROVIDER:
  246.       if (test_admin(curr_uid)) { /* NOT PROVIDER, but admin */
  247.     strcpy (curr_uid, "");
  248.     send_msg(msglist[ OK ], cur_sock);
  249.       } else
  250.     send_msg(msglist[ NOT_AUTH ], cur_sock);
  251.       break;
  252.  
  253.     case T_RELOAD: 
  254.       if (test_admin(curr_uid)) {
  255.     datastruct_load(1);      /* 1 == free old stuff first */
  256.     send_msg(msglist[ OK ], cur_sock);
  257.       } else
  258.     send_msg(msglist[ NOT_AUTH ], cur_sock);
  259.       break;              
  260.  
  261.     case T_SAVEWEB: /* save the web to disk */ {
  262.       if (test_admin(curr_uid)) {
  263.     save_datastruct();
  264.     send_msg(msglist[ OK ], cur_sock);
  265.       } else
  266.     send_msg(msglist[ NOT_AUTH ], cur_sock);
  267.       break;
  268.     }
  269.  
  270.     case T_SHOW_CONN:
  271.       send_connections(cur_sock);  /* NOT an admin function */
  272.       break;
  273.  
  274.     case T_GET_SERVER_INFO:
  275.       send_servers_list(cur_sock);
  276.       break;
  277.  
  278.       /* Not implementing the following */
  279.     case T_CHG_BANNER:
  280.     case T_SHUTDOWN:
  281.     case T_OUTPUTFMT:
  282.     case T_CHGD_SINCE:
  283.     case T_FINDKEY:
  284.     case T_SET_DATES:
  285.     case T_SOURCE_SRCH:
  286.     default:
  287.       send_msg(msglist[ HUH ], cur_sock);
  288.       break;
  289.  
  290.       } /* switch on transtype */
  291. }
  292.  
  293.  
  294. static void
  295. send_servers_list(int cur_sock)
  296. {
  297.   FILE           *fopen(), *srvfile;
  298.   char           buf[BUFSIZ],*cp;
  299.   int            num = 0;
  300.   
  301.   srvfile = fopen(SERVER_FILE, "r");
  302.   if (!srvfile) {
  303.     perror(SERVER_FILE);
  304.     send_msg(msglist[ CANT_FIND_SERVER_FILE ], cur_sock);
  305.     return;
  306.   }
  307.  
  308.   /* how many lines in the file? */
  309.   for (num = 0; fgets(buf,BUFSIZ-1,srvfile) != NULL; num++);
  310.   rewind (srvfile);
  311.  
  312.   cp = (char *) domalloc ((num+1) * BUFSIZ);  /* a little extra room */
  313.   sprintf (cp, "%d:servers\n", num);
  314.  
  315.   while (fgets(buf, BUFSIZ-1, srvfile) != NULL) 
  316.     strcat (cp, buf);
  317.   strcat (cp, ".\r\n");
  318.  
  319.   fclose(srvfile);
  320.   sendbuf (cp, strlen(cp), cur_sock);
  321.   return;
  322. }
  323.  
  324.  
  325. /*
  326.   send_nlistmsg_trav should only be used when the TI client
  327.   is expecting an nlist as the response to T_TRAVERSE.
  328.   TI client is expecting:
  329.   #nodes:
  330.   0:parent
  331.   1:child1
  332.   1:child2
  333.   .
  334.   */
  335.  
  336. static void
  337.   send_nlistmsg_trav (long par, long child, int repeatpar, int flgsfmt, int cur_sock)
  338. {
  339.   char *nlistptr;
  340.  
  341.   if (repeatpar) {
  342.     nlistptr = (char *) domalloc (3*BUFSIZ);
  343.     sprintf (nlistptr, "3:\n0:");
  344.   }
  345.   else {
  346.     nlistptr = (char *) domalloc (2*BUFSIZ);
  347.     sprintf (nlistptr, "2:\n0:");
  348.   }
  349.  
  350.   strcat (nlistptr, nodeinfostr(par, (struct s1 *)NULL, flgsfmt));
  351.   strcat (nlistptr, "\n1:");
  352.   if (repeatpar) {
  353.     strcat (nlistptr, nodeinfostr(par, (struct s1 *)NULL, flgsfmt));
  354.     strcat (nlistptr, "\n1:");
  355.   }
  356.   strcat (nlistptr, nodeinfostr(child, (struct s1 *) NULL, flgsfmt));
  357.   strcat (nlistptr, "\n.\r\n");
  358.   sendbuf (nlistptr, strlen(nlistptr), cur_sock);
  359.  
  360.   /* Assumption: sendbuf will free the nlistptr allocated memory */
  361. }
  362.  
  363.  
  364.  
  365. /* send_nlistmsg_find should only be used when the TI client
  366.    is expecting an nlist as the response to T_FIND.
  367.    TI client is expecting:
  368.    #nodes:
  369.    0:node1
  370.    0:node2
  371.    .
  372. */
  373.  
  374. static void send_nlistmsg_find (long par, long child, int flgsfmt, int cur_sock)
  375. {
  376.   char *nlistptr;
  377.  
  378.   nlistptr = (char *) domalloc (2*BUFSIZ);
  379.  
  380.   sprintf (nlistptr, "2:\n0:");
  381.   strcat (nlistptr, nodeinfostr(par, (struct s1 *)NULL, flgsfmt));
  382.   strcat (nlistptr, "\n0:");
  383.   strcat (nlistptr, nodeinfostr(child, (struct s1 *) NULL, flgsfmt) );
  384.   strcat (nlistptr, "\n.\r\n");
  385.   sendbuf (nlistptr, strlen(nlistptr), cur_sock);
  386.  
  387.   /* Assumption: sendbuf will free the nlistptr allocated memory */
  388. }
  389.  
  390.  
  391.  
  392.  
  393. /* send the file associated with a certain node to the client. */
  394. static void send_file(CONN *conn, char trans_type, int num_transfields,
  395.             char **transfields)
  396. {
  397.   int nodeid;
  398.   struct s1 *nd;
  399.   int cur_sock = conn->c_socket;
  400.   int flgsfmt = conn->c_output_fmt;
  401.  
  402.   if (num_transfields < 2)
  403.     nodeid = 0;
  404.   else 
  405.     nodeid = atoi (transfields[1]);
  406.  
  407.   if (!send_reserved_node(trans_type, num_transfields, transfields, nodeid, flgsfmt, cur_sock)) {
  408.     nd = getnode_bynodeid(nodeid);
  409.  
  410.     if (nd == NULL) {
  411.       send_empty_document(cur_sock);
  412.       return;
  413.     }
  414.  
  415.     switch (nd->gophertype) {
  416.     case GOPHTYP_GIF:
  417.     case GOPHTYP_IMAGE:
  418.     case GOPHTYP_TEXT:
  419.       send_gopher_info(nd, num_transfields, transfields, conn);
  420.       break;
  421.  
  422.     case GOPHTYP_CSO: 
  423.     case GOPHTYP_MACHQX:
  424.     case GOPHTYP_DOSBIN:
  425.     case GOPHTYP_UUENC:
  426.     case GOPHTYP_BINARY:
  427.     case GOPHTYP_SOUND:
  428.     case GOPHTYP_EVENT:
  429.     case GOPHTYP_CALENDAR:
  430.     case GOPHTYP_HTML:
  431.     case GOPHTYP_MIME:
  432.       send_reserved_node (trans_type, num_transfields, transfields,
  433.               GOPHERFILETYPES_UNAVAILABLE, flgsfmt, cur_sock);
  434.       break;
  435.  
  436.     case GOPHTYP_TELNET:
  437.     case GOPHTYP_TN3270:
  438.       send_reserved_node (trans_type, num_transfields, transfields, CLIENT_NEEDS_TELNET, flgsfmt, cur_sock);
  439.       break;
  440.  
  441.     case GOPHTYP_MENU:
  442.     case GOPHTYP_SEARCH:
  443.       send_msg (msglist[ NOT_DOCUMENT ], cur_sock);
  444.       break;
  445.       
  446.     default:
  447.       send_reserved_node (trans_type, num_transfields, transfields, 
  448.               UNKNOWN_DOC_TYPE, flgsfmt, cur_sock);
  449.       break;
  450.     }
  451.   }
  452. }
  453.  
  454.  
  455.  
  456.  
  457. void send_empty_document(int sock)
  458. {
  459.   nio_send_file (sock, "/dev/null", 0, 32000, 0);
  460. }
  461.  
  462.  
  463. void send_empty_menu (int sock)
  464. {
  465.   send_msg ("0:", sock);
  466. }
  467.  
  468.  
  469.  
  470. static int compute_nodeflags (struct s1 *nd, int format_type)
  471. {
  472.   if (format_type == NO_FLAGS_FORMAT) 
  473.     return 0;
  474.   
  475.   else if (nd == NULL)
  476.     return N_FAKE;  /* Not a real node */
  477.   
  478.   else {
  479.     switch (nd->gophertype) {
  480.       /*
  481.     Most of the N_ flags don't appear here even though MIT has
  482.     put them into the node.h file in the source distribution.
  483.     None of their code handles the N_ flags yet (except for N_IMAGE).
  484.     N_TELNETSESSION is my own creation. --lam
  485.     */
  486.       
  487.     case GOPHTYP_GIF:
  488.     case GOPHTYP_IMAGE:
  489.       return N_IMAGE;
  490.       break;
  491.       
  492.     case GOPHTYP_TELNET:
  493.     case GOPHTYP_TN3270:
  494.       return N_TELNETSESSION; 
  495.       break;
  496.       
  497.     default:
  498.       return 0;
  499.       /* N_TEXT and N_MENU flags are ignored by TI clients
  500.      at the moment (Feb 1993), so I'm leaving them out. */
  501.       break;
  502.     }
  503.   }
  504. }
  505.  
  506.  
  507.  
  508. static char *reserved_nodeinfostr(long nid, int flgfmt)
  509.      /* ASSUMPTION: the values in reserved nodes don't contain any extraneous
  510.     DLM characters */
  511. {
  512.   int flags;
  513.   extern struct resvnode *get_resvnode();
  514.   struct resvnode *rnode;
  515.  
  516.   rnode = get_resvnode (nid);
  517.  
  518.   if (rnode == NULL) {
  519.     rnode = get_resvnode(DUMMY_NODE);
  520.   }
  521.  
  522.   /*
  523.     MIT's code doesn't seem to handle these N_ flags...so just
  524.     use 0 as the flags and ignore flgsfmt
  525.  
  526.     if (cur_format_type == NO_FLAGS_FORMAT)
  527.     flags = 0;
  528.     else if (*(rnode->file))
  529.     flags = N_TEXT;
  530.     else 
  531.     flags = N_MENU;
  532.     */
  533.   
  534.   flags = 0;
  535.   /*nodeid:flags:date:keyword list:title:source:locker:file:parents:children*/
  536.  
  537.   if (*(rnode->file)) 
  538.     sprintf (nodeinfobuf, "%d:%d:%d::%s.:%s:none:%s::",
  539.          nid, flags, todaysdate, rnode->title,
  540.          GW_SOURCENAME, rnode->file);
  541.   else
  542.     sprintf (nodeinfobuf, "%d:%d:%d::%s/:%s::::", 
  543.          nid, flags, todaysdate, rnode->title,
  544.          GW_SOURCENAME);
  545.  
  546.   return (nodeinfobuf);
  547. }
  548.  
  549.  
  550. char *nodeinfostr (long nodeid, struct s1 *nd, int flgsfmt)
  551. {
  552.   long nodeflags;
  553.   char gophertype;
  554.   char gophertitle[BUFSIZ];
  555.   char gopherpath[BUFSIZ];
  556.   char gopherserver[BUFSIZ];
  557.   char gopherport[BUFSIZ];
  558.  
  559.   if (is_reserved_node (nodeid))
  560.     return (reserved_nodeinfostr(nodeid, flgsfmt));
  561.  
  562.   if (nd == NULL) { /* wasn't already resolved, so find it */
  563.     nd = getnode_bynodeid(nodeid);
  564.   }
  565.  
  566.   nodeflags = compute_nodeflags (nd, flgsfmt);
  567.  
  568.   if (nd == (struct s1 *) NULL) {  /* still not found; it's a bogus nodeid */
  569.     return (reserved_nodeinfostr(DUMMY_NODE, flgsfmt));
  570.   }
  571.  
  572.   else { 
  573.     /* there is no way to quote DLM character in the TechInfo
  574.        protocol at this time.  So convert it to something
  575.        else and let's hope it all works out */
  576.  
  577.     substi_char (nd->gophertitle,   gophertitle,  DLM, ';');
  578.     substi_char (nd->gopherpath,    gopherpath,   DLM, ';');
  579.     substi_char (nd->gopherserver,  gopherserver, DLM, ';');
  580.     substi_char (nd->gopherport,    gopherport,   DLM, ';');
  581.     if (nd->gophertype == DLM) gophertype = ';';
  582.     else gophertype = nd->gophertype;
  583.  
  584.     switch (nd->gophertype)
  585.       {
  586.       case GOPHTYP_MENU:
  587.       case GOPHTYP_SEARCH:
  588.     sprintf (nodeinfobuf, "%d:%d:%d:%c %s %s %s:%s%s:%s::::",
  589.          nd->nodeid, nodeflags, todaysdate, 
  590.          
  591.          gophertype, gopherpath, gopherserver, gopherport,
  592.          /* YUCK! use keywords fields for gopher info */
  593.          
  594.          gophertitle, find_abbrev(nd->gophertype),
  595.          GW_SOURCENAME);
  596.     
  597.     /* no locker, no filename, no parents, no children */
  598.     break;
  599.     
  600.       case GOPHTYP_CSO:
  601.       case GOPHTYP_TEXT:
  602.       case GOPHTYP_GIF:
  603.       case GOPHTYP_IMAGE:
  604.       case GOPHTYP_MACHQX:
  605.       case GOPHTYP_DOSBIN:
  606.       case GOPHTYP_BINARY:
  607.       case GOPHTYP_UUENC:
  608.       case GOPHTYP_TELNET:
  609.       default:
  610.     sprintf (nodeinfobuf, "%d:%d:%d:%c %s:%s%s:%s:%s:%s %s::",
  611.          nd->nodeid, nodeflags, todaysdate,
  612.          
  613.          gophertype, gopherport,
  614.          /* YUCK!!! use keywords field for some gopher info */
  615.          
  616.          gophertitle, find_abbrev(nd->gophertype),
  617.          GW_SOURCENAME,
  618.          "none",
  619.          /* put rest of gopher info in filename */
  620.          gopherpath, gopherserver);
  621.     /* no parents, no children */
  622.     break;
  623.       }
  624.   } /* not a bogus nodeid */
  625.   
  626.   return (nodeinfobuf);  /* static buf */
  627. }
  628.  
  629.  
  630.  
  631. static void
  632. send_node(int flgsfmt, int cur_sock, int num_transfields, char **transfields)
  633.      /* send all information about a particular node */
  634. {
  635.   long nodeid;
  636.   /* send node information:
  637.      Unfortunately, we don't have a record of the parents & children 
  638.      because we don't really have a web */
  639.   
  640.   if (num_transfields < 2)
  641.     nodeid = MAINMENUNODE;
  642.   else
  643.     nodeid = atoi (transfields[1]);
  644.  
  645.   send_msg (nodeinfostr(nodeid, (struct s1 *) NULL, flgsfmt), cur_sock);
  646. }
  647.  
  648.  
  649. /* write the transaction to the log file */
  650. void log_trans(char *line)
  651. {
  652.     char            logline[BUFSIZ];
  653.     time_t          secs;
  654.     int             dfd;
  655.  
  656.     time(&secs);
  657.     sprintf(logline, "%s:%s", line, ctime(&secs));
  658.     if ((dfd = open(TRANS_LOG, O_APPEND | O_CREAT | O_WRONLY, 0644)) == 0)
  659.         printf("error logging transaction\n");
  660.     write(dfd, logline, strlen(logline));
  661.     close(dfd);
  662.  
  663.     if (debug)
  664.       fprintf(stderr, "Transaction: %s", logline);
  665.     return;
  666. }
  667.  
  668.  
  669. /*
  670.   search--search for target.  If no node
  671.   is given, use global Gopher search node(veronica).
  672.   Called for keyword search, wais-index text search,
  673.   title search.
  674.   */
  675. static void
  676.  find(CONN *conn, char trans_type, int num_transfields, char **transfields)
  677. {
  678.   long nodeid;
  679.   struct s1 *nd;
  680.   char tmpstr[BUFSIZ];
  681.   int cur_sock = conn->c_socket;
  682.   int flgsfmt = conn->c_output_fmt;
  683.   
  684.   /* blank target string? send empty menu */
  685.   if (num_transfields < 2 || strlen(transfields[1]) < 1) {
  686.     send_empty_menu(cur_sock);
  687.     return;
  688.   }
  689.  
  690.   if (num_transfields > 2) {
  691.     nodeid = atoi(transfields[2]);
  692.     if (nodeid == MAINMENUNODE)
  693.       nodeid = GLOBGOPH_SRCH_NODE;
  694.   }
  695.   else
  696.     nodeid = GLOBGOPH_SRCH_NODE;
  697.  
  698.   if (is_reserved_node(nodeid)) {
  699.     send_nlistmsg_find (nodeid, FILE_NOT_SEARCHABLE, flgsfmt, cur_sock);
  700.     return;
  701.   }
  702.  
  703.   nd = getnode_bynodeid (nodeid);
  704.   if (nd == NULL)
  705.     send_empty_menu(cur_sock);
  706.  
  707.   else {
  708.     switch (nd->gophertype) {
  709.     case GOPHTYP_SEARCH:
  710.       send_gopher_info(nd, num_transfields, transfields, conn);
  711.       break;
  712.     default:
  713.       send_nlistmsg_find (nodeid, FILE_NOT_SEARCHABLE, flgsfmt, cur_sock);
  714.       break;
  715.     }
  716.   }
  717. }
  718.  
  719.  
  720.  
  721. /* Change output format */
  722. static void 
  723. change_format(int *fmt, int cur_sock, int num_transfields, char **transfields)
  724. {
  725.   int format;
  726.  
  727.   if (num_transfields < 2) {
  728.     send_msg(msglist[ UNKNOWN_FORMAT ], cur_sock);
  729.     return;
  730.   }
  731.  
  732.   format = atoi(transfields[1]);
  733.   if ((format < 1) || (format > NUM_FORMATS))
  734.     {
  735.       send_msg(msglist[ UNKNOWN_FORMAT ], cur_sock);
  736.       return;
  737.     }
  738.   *fmt = format;
  739.   send_msg(msglist[ OK ], cur_sock);
  740. }
  741.  
  742.  
  743.  
  744. static void
  745.   traverse(CONN *conn, char trans_type, int num_transfields, char **transfields) 
  746. {
  747.   int direction;
  748.   long nodeid;
  749.   int levels;
  750.   struct s1 *nd;
  751.   int cur_sock = conn->c_socket;
  752.   int flgsfmt = conn->c_output_fmt;
  753.   
  754.   if (num_transfields < 4) { /* if not enough fields, send empty list */
  755.     send_empty_menu (cur_sock);
  756.     return;
  757.   }
  758.   
  759.   direction = atoi(transfields[1]);
  760.   nodeid = atoi(transfields[2]);
  761.   levels = atoi(transfields[3]);
  762.  
  763.   if (direction == TRAV_UP)
  764.     send_nlistmsg_trav (nodeid, NO_SHOW_PATH, 0, flgsfmt, cur_sock);
  765.  
  766.   else if (direction == TRAV_OUT)
  767.     send_nlistmsg_trav (nodeid, NO_SHOW_PATH, 0, flgsfmt, cur_sock); 
  768.  
  769.   else if (levels > 1)
  770.     send_nlistmsg_trav (nodeid, NO_OUTLINE, 0, flgsfmt, cur_sock);
  771.  
  772.   else { /* traverse down ONE level */
  773.     if (!send_reserved_node(trans_type, num_transfields, transfields,
  774.                 nodeid, flgsfmt, cur_sock)) {
  775.       nd = getnode_bynodeid (nodeid);
  776.       if (nd == NULL) /* client gave unknown nodeid, it loses */
  777.     send_empty_menu(cur_sock);
  778.       else {
  779.     switch (nd->gophertype)
  780.       {
  781.       case GOPHTYP_MENU:
  782.         send_gopher_info (nd, num_transfields, transfields, conn);
  783.         break;
  784.  
  785.       case GOPHTYP_SEARCH:
  786.         send_nlistmsg_trav (nodeid,TRY_SEARCHCMD, 1, flgsfmt, cur_sock);
  787.         break;
  788.  
  789.       default:
  790.         send_nlistmsg_trav (nodeid, FILE_NOT_AMENU, 1, flgsfmt, cur_sock);
  791.         break;
  792.       }
  793.       } /* not unknown */
  794.     } /* handle reserved */
  795.   } /* traverse down one level */
  796. }
  797.  
  798.  
  799.  
  800. /* find the version number of the client associated with a certain machine */
  801. static void
  802.  find_ver(CONN *conn, int num_transfields, char **transfields) 
  803. {
  804.     FILE           *fopen(), *verfile;
  805.     char            verline[ADMIN_LN_SZ];
  806.     int             len;
  807.     int             cur_sock = conn->c_socket;
  808.  
  809.     verfile = fopen(VER_FILE, "r");
  810.     if (!verfile) {
  811.       send_msg(msglist[ VERSION_NOT_FOUND ], cur_sock);
  812.       return;
  813.     }
  814.  
  815.     if (num_transfields < 2 || strlen(transfields[1]) == 0) {
  816.       conn->c_type = "";
  817.       send_msg(msglist[ VERSION_NOT_FOUND ], cur_sock);
  818.       return;
  819.     }
  820.  
  821.     len = strlen(transfields[1]);
  822.     /* set c_type in conntab */
  823.     conn->c_type = domalloc((unsigned int) len+1);
  824.     strcpy(conn->c_type, transfields[1]);
  825.     
  826.     while (fgets(verline, ADMIN_LN_SZ, verfile) != '\0') {
  827.         verline[strlen(verline) - 1] = '\0';
  828.         if (strncasecmp(transfields[1], verline, len) == 0) {
  829.             fclose(verfile);
  830.             send_msg(verline, cur_sock);
  831.             return;
  832.         }
  833.     }
  834.     fclose(verfile);
  835.     send_msg(msglist[ VERSION_NOT_FOUND ], cur_sock);
  836.     return;        /* close file */
  837. }
  838.  
  839.  
  840.  
  841.  
  842. static void send_src_info(int cur_sock)
  843. {
  844.   char sourceline[BUFSIZ];
  845.  
  846.   /* source:longsrc:name:phone:email */
  847.   sprintf (sourceline, "%s:%s:::%s",
  848.        GW_SOURCENAME, GW_LONGSRCNAME, GW_EMAILADDR);
  849.   send_msg (sourceline, cur_sock);
  850. }
  851.  
  852.  
  853.   /* BUFFERSIZE must be big enough to handle all possible connections
  854.      + a header */
  855. #define BUFFERSIZE (200 + FD_SETSIZE*110)
  856.  
  857. /* Send a description of the current connections */
  858. void
  859. send_connections(int cur_sock) 
  860. {
  861.   int i;
  862.   char *buf, *prov, *cp;
  863.   time_t  secs;
  864.   extern CONN     conntab[];
  865.   
  866.   buf = (char *) domalloc((unsigned) BUFFERSIZE);
  867.   bzero(buf, BUFFERSIZE);
  868.  
  869.   sprintf(buf,"\
  870. %-5s %-5s  %-7s %-8s  %-25s  %-15s %s\n",
  871. "Conn#", "Sock", "Sesslen", "Inactive", "Host", "ClientType", "Prov");
  872.   
  873.   time(&secs);
  874.   for (i = 0; i < FD_SETSIZE; i++) 
  875.     {
  876.       if (conntab[i].c_socket != -1)
  877.     {
  878.       cp = index(buf,'\0');
  879.       if (conntab[i].c_flags & C_PROVIDER)
  880.         prov = "PROV";
  881.       else
  882.         prov = "";
  883.       sprintf(cp,"\
  884. %-5d %-5d  %-7.1f %-8.1f  %-25s  %-15s %s\n"
  885.           ,i,conntab[i].c_socket,
  886.           (float) ((secs - conntab[i].c_made) /60.0),
  887.           (float) ((secs - conntab[i].c_last) /60.0),
  888.           conntab[i].c_hostname, 
  889.           conntab[i].c_type,
  890.           prov);
  891.     }
  892.     }
  893.   /* 
  894.    * Put End-of-Message on buffer & send it out.  sendbuf() frees the
  895.    *  buffer for us.
  896.    */
  897.   strcat(buf, EOM);
  898.   sendbuf(buf, strlen(buf), cur_sock);
  899. }
  900. #undef BUFFERSIZE
  901.  
  902.  
  903.  
  904.  
  905. /* test to see if a uid is an admin */
  906. static int test_admin(char *passwd)
  907. {
  908.   FILE           *fopen(), *adminfile;
  909.   char            adminline[ADMIN_LN_SZ];
  910.  
  911.   adminfile = fopen(ADMIN_FILE, "r");
  912.   if (!adminfile) {
  913.     perror(ADMIN_FILE);
  914.     return 0;
  915.   }
  916.   while (fgets(adminline, ADMIN_LN_SZ, adminfile) != '\0') {
  917.     adminline[strlen(adminline) - 1] = '\0';
  918.     if (strcasecmp(passwd, adminline) == 0) {
  919.       fclose(adminfile);
  920.       return 1;
  921.     }
  922.   }
  923.   fclose(adminfile);
  924.   return 0;        /* close file */
  925. }
  926.  
  927. /* If the client's uid is ok, establish him as an administrator. */
  928. static void admin(int num_transfields, char **transfields, int cur_sock)
  929. {
  930.   if (num_transfields < 2 || *(transfields[1]) == 0)
  931.     send_msg(msglist[ NOT_AUTH ], cur_sock);
  932.   else if (test_admin(transfields[1])) {
  933.     strcpy(curr_uid, transfields[1]);
  934.     send_msg(msglist[ OK ], cur_sock);
  935.   }
  936.   else
  937.     send_msg(msglist[ NOT_AUTH ], cur_sock);
  938. }
  939.  
  940.  
  941.  
  942. /* nd is the parent or the search node.
  943.    if nd's gophertype is menu, write numnodes+1 into the buf 
  944.    because the parent node itself should be in there.
  945.    nlist is the list of children (or a results list).
  946.    */
  947. static void
  948. send_nlist (struct s1 *nd, long *nlist, int numnodes, int flgsfmt, int cur_sock)
  949. {
  950.   char *buf;
  951.   char *levelstr;
  952.   int n;
  953.   struct s1 *node;
  954.  
  955.   if (nd == NULL) {
  956.     fprintf (stderr, "send_nlist() called with null nd parameter\n");
  957.     return;
  958.   }
  959.  
  960.   if (nd->lastused != todaysdate) {
  961.     nd->lastused = todaysdate; /* node was selected */
  962.     tables_changed++;
  963.     lastused_changed++;
  964.   }
  965.  
  966.   /* assumption: average length of nodeinfo is less than 500 chars */
  967.   buf = (char *) malloc (500 * (numnodes+1));
  968.  
  969.   if (nd->gophertype == GOPHTYP_MENU) {
  970.     sprintf (buf, "%d:\n0:%s\n", numnodes+1,
  971.          nodeinfostr(nd->nodeid, nd, flgsfmt));
  972.     levelstr = "1:";
  973.   }
  974.   else {
  975.     sprintf (buf, "%d:\n", numnodes);
  976.     levelstr = "0:";
  977.   }
  978.  
  979.   for (n = 0; n < numnodes; n++) {
  980.     strcat (buf, levelstr);
  981.  
  982.     node = getnode_bynodeid (nlist[n]);
  983.  
  984.     /* item appeared on a menu, so update its "usedness" */
  985.     if (node != NULL) {
  986.       if (node->lastused != todaysdate) {
  987.     node->lastused = todaysdate; /* node is used as part of a menu */
  988.     tables_changed++;
  989.     lastused_changed++;
  990.       }
  991.     }
  992.     
  993.     strcat (buf, nodeinfostr(nlist[n], node, flgsfmt));
  994.     strcat (buf, "\n");
  995.   }
  996.   strcat (buf, ".\r\n");
  997.   sendbuf(buf, strlen(buf), cur_sock);
  998.   /* assuming sendbuf will free the allocated buf */
  999. }
  1000.  
  1001.  
  1002. send_cached_nlist (struct s1 *nd, int cur_sock, char *cachefilename, int flgsfmt)
  1003. {
  1004.   int numnodes;
  1005.   FILE *cafile;
  1006.   char line[100];
  1007.   long *nlist;
  1008.   int n;
  1009.  
  1010.   cafile = fopen(cachefilename, "r");
  1011.   if (cafile == NULL) {  /* not sure what to do at this point */
  1012.     fprintf (stderr, "Unable to read cache file %s!\n", cachefilename);
  1013.     return;
  1014.   }
  1015.   for (numnodes=0; fgets(line, sizeof(line)-1, cafile) != NULL; )
  1016.     numnodes++;
  1017.  
  1018.   nlist = (long *) domalloc ((unsigned int) numnodes * sizeof (long));
  1019.   n = 0;
  1020.   for (rewind(cafile); fgets(line, sizeof(line)-1, cafile) != NULL; ) {
  1021.     nlist[n] = atol(line);
  1022.     n++;
  1023.   }
  1024.  
  1025.   send_nlist (nd, nlist, numnodes, flgsfmt, cur_sock);
  1026.   free(nlist);
  1027.   fclose (cafile);
  1028. }
  1029.  
  1030.  
  1031.  
  1032.  
  1033. /*  handles 5 types: MENU, TEXT, GIF, IMAGE, SEARCH */
  1034. /* side effect: forks child, sets conn->c_hptr  */
  1035. static void send_gopher_info(struct s1 *nd, 
  1036.               int num_transfields, char **transfields,
  1037.               CONN *curr_conn)
  1038. {
  1039.   struct stat stbuf;
  1040.   char cachefilename[MAXPATHLEN];
  1041.   long startpoint = 0, requested = 32000;
  1042.   WAITFORHELPER *hptr;
  1043.   pid_t pid;
  1044.   char *path;
  1045.   int cur_sock = curr_conn->c_socket;
  1046.   int flgsfmt = curr_conn->c_output_fmt;
  1047.  
  1048.   if (nd == NULL)
  1049.     return;   /* should probably send a message or something */
  1050.  
  1051.   if (nd->lastused != todaysdate) {
  1052.     nd->lastused = todaysdate;  /* node was selected */
  1053.     tables_changed++;
  1054.     lastused_changed++;
  1055.   }
  1056.  
  1057.   if (nd->gophertype == GOPHTYP_TEXT ||
  1058.       nd->gophertype == GOPHTYP_GIF ||
  1059.       nd->gophertype == GOPHTYP_IMAGE) { /* not for menus & searches */
  1060.     /* get startpt & length from transaction */
  1061.     if (num_transfields > 2) startpoint = atoi(transfields[2]);
  1062.     if (num_transfields > 3) requested = atoi(transfields[3]);
  1063.   }
  1064.   
  1065.   if (nd->gophertype == GOPHTYP_SEARCH) {
  1066.     /* assuming calling code already checked for blank search string */
  1067.     path = domalloc(strlen(transfields[1]) + 1 +
  1068.             strlen(nd->gopherpath) + 1);
  1069.     sprintf (path, "%s\t%s", nd->gopherpath, transfields[1]);
  1070.     sprintf (cachefilename, "%s%d.%c.%s", CACHEDIR, nd->nodeid, 
  1071.          nd->gophertype, transfields[1]);
  1072.   }
  1073.  
  1074.   else { /* if type is something other than SEARCH */
  1075.     path = domalloc(strlen(nd->gopherpath) + 1);
  1076.     sprintf (path, "%s", nd->gopherpath);
  1077.     sprintf (cachefilename, "%s%d.%c", CACHEDIR, nd->nodeid, 
  1078.          nd->gophertype);
  1079.   }
  1080.   
  1081.   /* Can we use cached file? */
  1082.   
  1083.   if (stat(cachefilename, &stbuf) == 0 &&
  1084.       ((time(0) - stbuf.st_mtime) < CACHETIMEOUT))  {   /* use cached file */
  1085.     
  1086.     if (nd->gophertype == GOPHTYP_MENU ||
  1087.     nd->gophertype == GOPHTYP_SEARCH) 
  1088.       send_cached_nlist (nd, cur_sock, cachefilename, flgsfmt);
  1089.     
  1090.     else /* TEXT, GIF, or IMAGE */
  1091.       nio_send_file (cur_sock, cachefilename, startpoint, requested, 0);
  1092.     
  1093.     return;                       /* DONE ! */
  1094.   }  /* use cache file */
  1095.  
  1096.  
  1097.   /* if we got here, we couldn't use the cache file.
  1098.      Need to call a helper. Create c_hptr for the connection */
  1099.  
  1100.   /* Create and initialize c_hptr for curr_conn */
  1101.   hptr = (WAITFORHELPER *) domalloc ((unsigned int)sizeof (WAITFORHELPER));
  1102.   curr_conn->c_hptr = hptr;
  1103.  
  1104.   hptr->file = tempnam(CACHEDIR, "GOPHER");
  1105.   hptr->node = nd;
  1106.   /* ASSUMING that nothing will remove nd from the tables between
  1107.      now and the time that the help returns ! */
  1108.   hptr->startpoint = startpoint;
  1109.   hptr->requested = requested;
  1110.   hptr->cachefile = (char *) domalloc (strlen(cachefilename) + 1);
  1111.   strcpy (hptr->cachefile, cachefilename);
  1112.  
  1113.   
  1114.   pid = fork ();
  1115.   if (pid == 0) {    /* child */
  1116.     execlp (HELPER, HELPER, nd->gopherserver, nd->gopherport, path,
  1117.         hptr->file, (char *)0);
  1118.     fprintf (stderr, "EXECLP FAILED!!!\n");
  1119.     perror (HELPER);
  1120.     exit (-1);  /* child had a problem */
  1121.   }
  1122.  
  1123.   else if (pid < 0) {  /* parent, but no child */
  1124.  
  1125.     /* what to send to TI client ???*/
  1126.  
  1127.     fprintf (stderr, "Unable to fork child\n");
  1128.     perror ("fork");
  1129.   }
  1130.  
  1131.   else {  /* parent */
  1132.     (curr_conn->c_hptr)->pid = pid;
  1133.     signal (SIGCHLD, catch_child);
  1134.     fprintf (stderr, "\nT=%d.  Set up helper pid %d for sock %d\n",
  1135.          time(0), (curr_conn->c_hptr)->pid, curr_conn->c_socket);
  1136.     do_free(path);
  1137.   }
  1138. }
  1139.  
  1140.  
  1141.  
  1142. static char *find_abbrev(char ch)
  1143. {
  1144.   int x;
  1145.  
  1146.   for (x = 0; x < NUM_GOPHER_ABBREV && ch != gopher_abbrev[x].gophtyp; x++);
  1147.   if (ch == gopher_abbrev[x].gophtyp)
  1148.     return (gopher_abbrev[x].gophabbrev);
  1149.   else
  1150.     return (gopher_abbrev_unk);
  1151. }
  1152.  
  1153.  
  1154. /* ASSUMPTION: T_SENDFILE, T_TRAVERSE, and T_HELP call send_reserved_node  */
  1155. static int
  1156. send_reserved_node (char trans_type, int num_transfields, char **transfields, long nodeid, int flgsfmt, int cur_sock)
  1157. {
  1158.   struct resvnode *rnode;
  1159.   int child;
  1160.   int starting = 0;
  1161.   int requested = 32000;
  1162.   char *nlistbuf;
  1163.  
  1164.   if (!is_reserved_node(nodeid))
  1165.     return 0;
  1166.  
  1167.   if (trans_type == T_SENDFILE) { /* t:nodeid:st:en */
  1168.     if (num_transfields > 2)
  1169.       starting = atoi (transfields[2]);
  1170.     if (num_transfields > 3)
  1171.       requested = atoi (transfields[3]);
  1172.   }
  1173.  
  1174.   rnode = get_resvnode(nodeid);
  1175.   if (rnode == NULL) {
  1176.     if (trans_type == T_SENDFILE)
  1177.       send_empty_document(cur_sock);
  1178.     else if (trans_type == T_TRAVERSE)
  1179.       send_empty_menu(cur_sock);
  1180.     return 1;
  1181.   }
  1182.   
  1183.   else { /* found reserved node */
  1184.     if (trans_type == T_SENDFILE) {
  1185.       if (*(rnode->file)) {
  1186.     nio_send_file(cur_sock, rnode->file, starting, requested, 0);
  1187.       }
  1188.       else
  1189.     send_msg (msglist[ NOT_DOCUMENT ], cur_sock);
  1190.     }
  1191.  
  1192. /* add code around here to dynamically generate an nlist
  1193.    of all the gopher nodes which are GOPHTYP_SEARCH */
  1194.  
  1195.     else if (trans_type == T_TRAVERSE || trans_type == T_HELP) {
  1196.       nlistbuf = (char *) domalloc ((rnode->numchildren + 1)* BUFSIZ);
  1197.       
  1198.       /*ASSUMPTION: the average length of the nodeinfo for the
  1199.     reserved node and its children is less than BUFSIZ*/
  1200.       
  1201.       /*Assumption: we aren't automatically adding HelpMenu and
  1202.     Veronica to reserved node menus */
  1203.       
  1204.       sprintf (nlistbuf, "%d:\n", rnode->numchildren + 1);
  1205.       strcat (nlistbuf, "0:");
  1206.       strcat (nlistbuf, reserved_nodeinfostr(rnode->nodeid, flgsfmt));
  1207.       strcat (nlistbuf, "\n");
  1208.       for (child=0; child < rnode->numchildren; child++) {
  1209.     strcat (nlistbuf, "1:");
  1210.     strcat (nlistbuf, nodeinfostr(rnode->children[child],
  1211.                       (struct s1 *) NULL, flgsfmt));
  1212.     strcat (nlistbuf, "\n");
  1213.       }
  1214.       strcat (nlistbuf, ".\r\n");
  1215.       sendbuf (nlistbuf, strlen(nlistbuf), cur_sock);
  1216.       /* Assumption: sendbuf & its cronies will free the allocated
  1217.      space for nlistbuf */
  1218.     }
  1219.   }
  1220.   return 1;
  1221. }
  1222.  
  1223.